package xyz.paragon.product.persistence.service

import spock.lang.Narrative
import spock.lang.Specification
import spock.lang.Title
import xyz.paragon.product.persistence.domain.Product
import xyz.paragon.product.persistence.repository.ProductRepository
import xyz.paragon.product.persistence.service.impl.ProductServiceImpl

import javax.persistence.EntityNotFoundException

@Title("Product Service Implementation Specification")
@Narrative("Specification of what product service implementation should be capable of")
class ProductServiceImplSpec extends Specification {

    private ProductService productService;
    private ProductRepository productRepository;

    void setup() {
        def products = [
            new Product(1,"product1", "description1"),
            new Product(2,"product2", "description2"),
            new Product(3,"product3", "description3"),
            new Product(4,"product4", "description4"),
            new Product(5,"product5", "description5"),
            new Product(6,"product6", "description6"),
            new Product(7,"product7", "description7"),
            new Product(8,"product8", "description8"),
            new Product(9,"product9", "description9"),
            new Product(10,"product10", "description10")
        ]

        this.productRepository = Mock(ProductRepository.class)
        this.productRepository.findAll() >> {products}
        this.productRepository.findById(_ as Long) >> {Long id -> products.stream().filter({ p -> p.getId() == id }).findFirst()}
        this.productRepository.save(_ as Product) >> {Product product -> products.add(product);   return product}
        this.productRepository.deleteById(_ as Long) >> {Long id -> products.removeIf({ p -> p.getId() == id })}

        this.productService = new ProductServiceImpl(this.productRepository);
    }

    def "product service is capable of fetching all products"() {
        expect: "a total size of products fetched should be equal to ten"
        this.productService.findAll().size() == 10
    }

    def "product service is capable of fetching a product by id"() {
        when: "a product found should have a respective product name and if id is unknown then an exception is thrown"
        def product1 = this.productService.findById(5)
        def product2 = this.productService.findById(999)

        then: ""
        product1.getName() == "product5"
        thrown(EntityNotFoundException)
    }

    def "productService is capable of saving new products"() {
        when: "a product is saved with productService"
        this.productService.save(new Product(11, "product11", "description11"))

        then: "total size of products is larger by one and the last added product is product11"
        this.productService.findAll().size() == 11
        this.productService.findById(11).getName() == "product11"
    }

    def "productService is capable of updating products"() {
        when: "a product is updated with productService"
        def returnedProduct = this.productService.findById(3)
        def updatedProduct = returnedProduct.setDescription("updatedDescription3");
        this.productService.save(updatedProduct)

        then: "description of the updated product was updated"
        this.productService.findById(3).getDescription() == "updatedDescription3"
    }

    def "productService is capable of deleting products"() {
        when: "a product is deleted with productService"
        this.productService.deleteById(7)
        this.productService.findById(7)

        then: "an attempt to get the removed product should throw an exception"
        this.productService.findAll().size() == 9
        thrown(EntityNotFoundException)
    }
}
